package fr.utt.lo02.CrazyEights.model.piles;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Observable;

import fr.utt.lo02.CrazyEights.model.Game;
import fr.utt.lo02.CrazyEights.model.cards.Card;
import fr.utt.lo02.CrazyEights.model.cards.CardRank;
import fr.utt.lo02.CrazyEights.model.cards.CardSuit;
import fr.utt.lo02.CrazyEights.model.exceptions.EmptyPileException;


/**
 * A class managing the StockPile.
 * 
 * @author Jeremy
 * 
 */

public class StockPile extends Observable implements Serializable{

	private static final long serialVersionUID = 1L;
	private ArrayList<Card> cardsInPile = new ArrayList<Card>();
	
	/**
	 * Create the stockPile
	 * @param nbOfPlayers Number of players in the game.
	 * @param nbOfCardsByPlayer Number of cards by player.
	 */
	public StockPile(int nbOfPlayers, int nbOfCardsByPlayer) {
		int decks = nbOfDecks(nbOfPlayers, nbOfCardsByPlayer);

		for(int i=0; i < decks; i++)
			addDeckToPile();

		shuffleCards();
	}

	/**
	 * Calculate the number of decks needed to play.
	 * @param nbOfPlayers the number of players in game
	 * @param nbOfCardsByPlayer the number of cards by player
	 * @return number of decks
	 */
	public int nbOfDecks(int nbOfPlayers, int nbOfCardsByPlayer) {
		int decks = 1;
		/*
		 * Normally we add a deck when we have more than 5 players
		 * with 8 cards so if we exceed 40 cards for players.
		 * We apply this rule but with varying numbers.
		 */
		
		while (nbOfPlayers * nbOfCardsByPlayer > 40 * decks)
			decks++;
		

		return decks;
	}
	
	/**
	 * We add *every* type of card in the pile : a deck
	 */
	public void addDeckToPile() {
		for(CardSuit s : CardSuit.values())
			if(s!=CardSuit.BLACK && s!=CardSuit.RED)
				for(CardRank r : CardRank.values())
					if(r != CardRank.Joker)
						cardsInPile.add(new Card(s,r));

		cardsInPile.add(new Card(CardSuit.RED, CardRank.Joker));
		cardsInPile.add(new Card(CardSuit.BLACK, CardRank.Joker));
	}
	
	public void shuffleCards() {
		Collections.shuffle(cardsInPile);
	}
	
	/**
	 * Draw only *one* card
	 */
	public Card draw() throws EmptyPileException {
		return draw(1).get(0);
	}
	
	/**
	 * This method is use to draw a set of Card from the stockPile
	 * @param nbOfCards the number of cards to draw
	 * @return the cards drawn
	 * @throws EmptyPileException
	 */
	public ArrayList<Card> draw(int nbOfCards) throws EmptyPileException {
		ArrayList<Card> cards = new ArrayList<Card>();
		
		// The ArrayList's ends of cardsInPile will be the top of the pile to avoid unnecessary calculations to change items indexes.

		if(cardsInPile.size()-nbOfCards<=0)
			refillPile();
		
		if(this.isEmpty())
			throw new EmptyPileException("The StockPile is empty");
		
		if(cardsInPile.size()>=nbOfCards){
			for(int i = 0; i < nbOfCards; i++) {
				Card d = cardsInPile.get(cardsInPile.size()-1);
				cards.add(d);
				cardsInPile.remove(cardsInPile.size()-1);
			}
			setChanged();
			notifyObservers();
		}
		
		return cards;
	}
	
	/**
	 * Check if the pile is empty
	 * @return true if the pile is empty, false otherwise
	 */
	public boolean isEmpty() {
		return cardsInPile.isEmpty();
	}
	
	public int getNbCardsInPile(){
		return cardsInPile.size();
	}
	
	/**
	 * Refill the pile with the cards in the discardPile
	 */
	public void refillPile(){
		cardsInPile.addAll(Game.getInstance().getDiscardPile().getExtraCardsAndEmpty());
		setChanged();
		notifyObservers();
	}
}
